home *** CD-ROM | disk | FTP | other *** search
- /*---------------------------------------------------------------------------------
-
- FILE:
- screen buffering.c
-
- DESCRIPTION:
- this is the implementation of a set of routines to update and use an offscreen
- buffer.
-
- COPYRIGHT:
- ©1992-5 Hugo M. Ayala
- ©1992-5 Apple Computer Inc.
-
- CHANGE LOG:
-
- 12/01/93 Hugo wrote entirely from scratch at home with a cold
- 07/13/94 Hugo fixed a bug while sitting in a room in Yokohama
- Japan while having a beer.
- 08/04/94 Hugo removed the dependencies from the quickdraw device loop
- 07/06/95 Hugo adapted for an article to appear in the develop journal
-
- ---------------------------------------------------------------------------------*/
-
- #include "screen buffering.h"
-
- #include "Memory.h"
-
- #include "graphics routines.h"
- #include "graphics toolbox.h"
- #include "math routines.h"
-
- #include "GXExceptions.h"
-
- #ifdef debugging
- #include "graphics debugging.h"
- #endif
-
- #pragma segment screen_buffering
-
- struct viewPortBufferRecord {
-
- gxViewGroup group; // the offscreen's viewgroup
- gxViewDevice device; // the offscreen viewDevice
- gxViewPort view; // the offscreen's viewPort. same map & clip as the screenview
-
- gxShape buffer; // the bitmap of the off-screen viewDevice
- gxBitmap bits; // the data structure from which the buffer shape was created
- Handle storage; // a handle in MF Temp mem that contains the bits of the above bitmap
-
- gxTransform offxform; // a transform that draws any shape into the off-screen
- gxTransform on_xform; // a transform that draws any shape on screen
-
- gxShape eraser; // a shape to erase the off-screen to the background color
- gxShape marker; // a picture used to draw any shape passed in to the off-screen
-
- gxShape updatearea; // a shape in the window's space that represents the area to update
-
- short usehalftone; // if we need to use a halftone instead of full depth
-
- WindowPtr window; // the window into which the viewport draws
- gxViewPort parent; // the document's parent viewport
- gxViewPort screenview; // viewPort that we've been ask to buffer
- gxShape page; // the shape that we're asked to draw
-
- gxRectangle bounds; // the bounds that the offscreen buffer covers
- gxMapping invmap; // the inverse of the offscreen viewport map
- gxPoint viewdelta; // the last delta for the offscreen viewport
- };
-
- typedef struct viewPortBufferRecord viewPortBufferRecord;
-
- /*---------------------------------------------------------------------------------
- EqualMapping
- ---------------------------------------------------------------------------------*/
- static boolean EqualMapping( const gxMapping *one, const gxMapping *two )
- {
- long *onePtr = (long *) & one->map;
- long *twoPtr = (long *) & two->map;
-
- short count;
-
- for( count = ( sizeof( gxMapping ) / sizeof( long ) - 1 ); count >= 0; count -= 1 )
- {
- if( *onePtr++ != *twoPtr++ )
- return( false );
- }
-
- return( true );
- }
- /*---------------------------------------------------------------------------------
- BufferDrawing
- ---------------------------------------------------------------------------------*/
- static void BufferDrawing( viewPortBufferRecord *sbPtr, const gxRectangle *boundsPtr,
- gxViewDevice target )
- {
- gxRectangle bounds = *boundsPtr;
-
- long depth;
- gxMapping map;
- gxMapping savemap;
- gxShape devsh;
- gxBitmap devbits;
-
- long size;
- OSErr status;
- long gxstatus;
-
- gxPoint viewloc;
- gxBitmap oldbits = sbPtr->bits;
-
- if( sbPtr->usehalftone )
- {
- devsh = nil; // so we don't try and dispose of it later
-
- depth = 1; // halftones are shallow
- sbPtr->bits.space = gxIndexedSpace; // let gx fill in the color set for us
- sbPtr->bits.set = nil;
- sbPtr->bits.profile = nil;
- }
- else
- {
- GXGetBitmap( devsh = GXGetViewDeviceBitmap( target ), &devbits, nil );
-
- depth = devbits.pixelSize; // we use the same bitdepth of the device
-
- sbPtr->bits.space = devbits.space;
- sbPtr->bits.set = devbits.set;
- sbPtr->bits.profile = devbits.profile;
- }
-
- // the bounds that we get to begin with are in the space of the local viewdevice
-
- GXGetViewDeviceMapping( target, &map );
- InvertMapping( &map, &map );
- MapPoints( &map, 2, (gxPoint *) &bounds );
- GXGetViewPortGlobalMapping( sbPtr->parent, &map );
- InvertMapping( &map, &map );
- MapPoints( &map, 2, (gxPoint *) &bounds );
-
- // now the bounds are in global space
-
- sbPtr->bits.width = FixedTruncate( bounds.right + 0xFFFF ) - FixedTruncate( bounds.left );
- sbPtr->bits.height = FixedTruncate( bounds.bottom + 0xFFFF ) - FixedTruncate( bounds.top );
- sbPtr->bits.rowBytes = ( ( sbPtr->bits.width * depth + 31 ) >> 5 ) << 2;
- sbPtr->bits.pixelSize = depth;
-
- // this point is expressed in global coordinates but we want it in page coordinates
-
- viewloc.x = bounds.left; // these numbers are already in window space
- viewloc.y = bounds.top;
-
- GXGetTransformMapping( sbPtr->on_xform, &savemap );
- map = savemap;
- MoveMappingTo( &map, viewloc.x, viewloc.y ); // this makes the os at 0,0 draw at x,y
-
- if( ! EqualMapping( &map, &savemap ) )
- {
- GXSetTransformMapping( sbPtr->on_xform, &map );
- }
-
- GXGetViewPortMapping( sbPtr->view, &map );
-
- if( ( map.map[2][0] != sbPtr->viewdelta.x - viewloc.x ) ||
- ( map.map[2][1] != sbPtr->viewdelta.y - viewloc.y ) )
- {
- // this makes the page at x,y draw at 0,0
-
- map.map[2][0] = sbPtr->viewdelta.x - viewloc.x; // this is like a MoveMapping( -viewloc )
- map.map[2][1] = sbPtr->viewdelta.y - viewloc.y;
-
- GXSetViewPortMapping( sbPtr->view, &map );
- }
-
- // we have all the entries of our offscreen bitmap set up except for allocating the actual bits
-
- size = sbPtr->bits.rowBytes * sbPtr->bits.height;
-
- check( size > 0 );
- if( sbPtr->storage )
- {
- if( (* (sbPtr->storage)) != nil )
- SetHandleSize( sbPtr->storage, size );
- else
- ReallocHandle( sbPtr->storage, size );
-
- nrequire( status = MemError(), TempBufferFailed );
- }
- else
- require( sbPtr->storage = TempNewHandle( size, &status ), TempBufferFailed );
-
- HNoPurge( sbPtr->storage );
- HLock( sbPtr->storage );
-
- sbPtr->bits.image = * ((void **) sbPtr->storage );
-
- // take a look to see if we need to invalidate all of the world when we do this
-
- if( oldbits.image != sbPtr->bits.image ||
- oldbits.width != sbPtr->bits.width ||
- oldbits.height != sbPtr->bits.height ||
- oldbits.rowBytes != sbPtr->bits.rowBytes ||
- oldbits.pixelSize != sbPtr->bits.pixelSize ||
- oldbits.space != sbPtr->bits.space ||
- ( oldbits.set != sbPtr->bits.set &&
- oldbits.set &&
- GXEqualColorSet( oldbits.set, sbPtr->bits.set ) == false ) ||
- ( oldbits.profile != sbPtr->bits.profile &&
- oldbits.profile &&
- GXEqualColorProfile( oldbits.profile, sbPtr->bits.profile ) == false ) )
- {
- GXSetBitmap( sbPtr->buffer, &sbPtr->bits, nil );
- GXSetViewDeviceBitmap( sbPtr->device, sbPtr->buffer );
- }
- else
- {
- sbPtr->bits.set = oldbits.set; // so we test this one instead of the one that got disposed
- sbPtr->bits.profile = oldbits.profile; // ditto
- }
-
- // erase the offscreen, draw the shape into it, and then copy it on screen
-
- GXDrawShape( sbPtr->eraser ); // erase
- GXDrawShape( sbPtr->marker ); // buffer
- GXDrawShape( sbPtr->buffer ); // transfer -- done
-
- HUnlock( sbPtr->storage );
- HPurge( sbPtr->storage );
-
- if( devsh ) GXDisposeShape( devsh ); // dispose the device bitmap
-
- GXGetGraphicsError( &gxstatus ); ncheck( gxstatus );
- if( gxstatus ) goto DrawingFailed;
-
- return;
-
- TempBufferFailed:
- GXDisposeShape( devsh ); // dispose the device bitmap
-
- DrawingFailed:
- GXDrawShape( sbPtr->updatearea );
- GXDrawShape( sbPtr->page );
- }
-
- /*---------------------------------------------------------------------------------
-
- NewViewPortWBuffer
-
- ---------------------------------------------------------------------------------*/
- viewPortBuffer NewViewPortWBuffer( WindowPtr window, gxViewPort view,
- const gxColor *backcolorPtr )
- {
- Handle sbHdl;
-
- if( sbHdl = NewHandleClear( sizeof( viewPortBufferRecord ) ) )
- {
- gxInk background;
-
- gxHalftone halftone;
-
- viewPortBufferRecord *sbPtr;
-
- HLock( sbHdl ); sbPtr = * (viewPortBufferRecord **) sbHdl;
-
- sbPtr->window = window;
- sbPtr->screenview = view;
- sbPtr->parent = GXGetViewPortParent( view );
-
- sbPtr->storage = nil; // we don't allocate storage until we need it
- sbPtr->buffer = GXNewShape( gxBitmapType );
- sbPtr->group = GXNewViewGroup();
- sbPtr->view = GXNewViewPort( sbPtr->group );
- sbPtr->device = GXNewViewDevice( sbPtr->group, sbPtr->buffer );
-
- if( sbPtr->usehalftone = GXGetViewPortHalftone( view, &halftone ) )
- {
- GXSetViewPortHalftone( sbPtr->view, &halftone );
- }
-
- sbPtr->offxform = GXNewTransform();
- GXSetTransformViewPorts( sbPtr->offxform, 1, &sbPtr->view );
-
- sbPtr->on_xform = GXNewTransform();
- GXSetTransformViewPorts( sbPtr->on_xform, 1, &sbPtr->parent );
-
- background = GXNewInk();
-
- if( backcolorPtr )
- {
- GXSetInkColor( background, backcolorPtr );
- }
- else
- {
- gxColor backcolor;
-
- backcolor.space = gxRGBSpace;
- backcolor.profile = nil;
- backcolor.element.rgb.red =
- backcolor.element.rgb.green =
- backcolor.element.rgb.blue = 0xFFFF
-
- GXSetInkColor( background, &backcolor );
- }
-
- sbPtr->eraser = GXNewShape( gxFullType );
- GXSetShapeInk( sbPtr->eraser, background );
- GXDisposeInk( background );
-
- /* the initial bounds for the offscreen are the entire window */
-
- sbPtr->bounds.left = ff( window->portRect.left );
- sbPtr->bounds.top = ff( window->portRect.top );
- sbPtr->bounds.right = ff( window->portRect.right );
- sbPtr->bounds.bottom = ff( window->portRect.bottom );
-
- sbPtr->updatearea = GXNewRectangle( & sbPtr->bounds );
- GXSetShapeViewPorts( sbPtr->updatearea, 1, & sbPtr->parent );
-
- sbPtr->marker = GXNewShape( gxPictureType );
-
- GXSetShapeTransform( sbPtr->eraser, sbPtr->offxform );
- GXSetShapeTransform( sbPtr->marker, sbPtr->offxform );
- GXSetShapeTransform( sbPtr->buffer, sbPtr->on_xform );
-
- ResetMapping( &sbPtr->invmap );
-
- /* the rest of the fields in the block are initilized to zero courtersy */
- /* of the "Clear" in the NewHandleClear used to allocate this block */
-
- HUnlock( sbHdl );
- }
-
- return((viewPortBuffer) sbHdl);
- }
-
- /*---------------------------------------------------------------------------------
-
- DisposeViewPortWBuffer
-
- ---------------------------------------------------------------------------------*/
- void DisposeViewPortWBuffer( viewPortBuffer sb )
- {
- viewPortBufferRecord *sbPtr;
-
- HLock((Handle) sb ); sbPtr = *sb;
-
- // we need to dispose of all of the things that we allocated
-
- GXDisposeShape( sbPtr->marker );
- GXDisposeShape( sbPtr->eraser );
-
- GXDisposeTransform( sbPtr->on_xform );
- GXDisposeTransform( sbPtr->offxform );
- GXDisposeViewDevice( sbPtr->device );
- GXDisposeViewPort( sbPtr->view );
- GXDisposeViewGroup( sbPtr->group );
-
- GXDisposeShape( sbPtr->buffer );
-
- if( sbPtr->storage ) DisposeHandle( sbPtr->storage );
-
- HUnlock((Handle) sb );
- DisposeHandle((Handle) sb );
- }
-
- /*---------------------------------------------------------------------------------
-
- UpdateViewPortWBuffer
-
- ---------------------------------------------------------------------------------*/
- void UpdateViewPortWBuffer( viewPortBuffer sb, gxShape clip, gxMapping *viewmap )
- {
- viewPortBufferRecord *sbPtr;
- gxHalftone halftone;
-
- HLock((Handle) sb ); sbPtr = *sb;
-
- if( viewmap )
- {
- GXSetViewPortMapping( sbPtr->view, viewmap );
- InvertMapping( &sbPtr->invmap, viewmap );
-
- sbPtr->viewdelta.x = viewmap->map[2][0];
- sbPtr->viewdelta.y = viewmap->map[2][1];
- }
-
- if( clip )
- {
- // the clip is in display space (local space for the window)
-
- GXSetShapeClip( sbPtr->updatearea, clip );
- GXGetShapeBounds( clip, 0, & sbPtr->bounds );
- }
- else
- {
- WindowPtr window = sbPtr->window;
-
- sbPtr->bounds.left = ff( window->portRect.left );
- sbPtr->bounds.top = ff( window->portRect.top );
- sbPtr->bounds.right = ff( window->portRect.right );
- sbPtr->bounds.bottom = ff( window->portRect.bottom );
- }
-
- if( sbPtr->usehalftone = GXGetViewPortHalftone( sbPtr->screenview, &halftone ) )
- GXSetViewPortHalftone( sbPtr->view, &halftone );
- else
- GXSetViewPortHalftone( sbPtr->view, nil );
-
- HUnlock((Handle) sb );
- }
-
- /*---------------------------------------------------------------------------------
-
- SetViewPortWBufferDither
-
- ---------------------------------------------------------------------------------*/
- void SetViewPortWBufferDither( viewPortBuffer sb, const long dither )
- {
- GXSetViewPortDither( (*sb)->view, dither );
- }
- /*---------------------------------------------------------------------------------
-
- DrawShapeBuffered
-
- This is the routine that implements the drawing of shapes through a
- doubled buffered viewport.
-
- If a rectangle is specified then it is treated as being inside of the space
- of the viewport which defines this record (sbPtr->view). as it turns out this
- is more work for this routine, since it has to compute the bounds as expressed
- in window space in order to figure out what it needs to draw. However, since
- most drawing and updating requests will come in the space of the window already,
- it seem correct to leave it to this function to figure out where drawing should
- happen.
-
- ---------------------------------------------------------------------------------*/
- void DrawShapeBuffered( viewPortBuffer sb, gxShape page, const gxRectangle *updatebounds )
- {
- viewPortBufferRecord *sbPtr;
- gxRectangle bounds;
-
- HLock((Handle) sb); sbPtr = *sb;
-
- if( updatebounds )
- {
- gxMapping map;
-
- GXGetViewPortMapping( sbPtr->screenview, &map );
- bounds = *updatebounds;
-
- bounds.left = bounds.left & 0xFFFF0000;
- bounds.right = ( bounds.right + 0xFFFF ) & 0xFFFF0000;
-
- bounds.top = bounds.top & 0xFFFF0000;
- bounds.bottom = ( bounds.bottom + 0xFFFF ) & 0xFFFF0000;
-
- MapPoints( &map, 2, (gxPoint *) &bounds );
-
- bounds.left = bounds.left & 0xFFFF0000;
- bounds.right = ( bounds.right + 0xFFFF ) & 0xFFFF0000;
-
- bounds.top = bounds.top & 0xFFFF0000;
- bounds.bottom = ( bounds.bottom + 0xFFFF ) & 0xFFFF0000;
- }
- else
- bounds = sbPtr->bounds;
-
- // the above given bounds are in the window space -- just right
-
- GXSetRectangle( sbPtr->updatearea, &bounds );
-
- // check to see that the shape is actually visible on the screen and the proceed to draw
-
- if( bounds.left < bounds.right && bounds.top < bounds.bottom )
- {
- GDHandle screen;
-
- if( sbPtr->page != page )
- {
- GXSetPicture( sbPtr->marker, 1, & page, nil, nil, nil );
- sbPtr->page = page;
- }
-
- if( screen = GetDeviceList() )
- {
- do
- {
- gxViewDevice device = GXGetGDeviceViewDevice( screen );
-
- // note that we re-use the bounds in here
-
- if( GXGetShapeDeviceBounds( sbPtr->updatearea, sbPtr->parent, device, &bounds ) )
- {
- BufferDrawing( sbPtr, &bounds, device );
- }
-
- } while( screen = GetNextDevice( screen ) );
- }
- }
- }